package de.wolvan.tools;

import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;

public class LinkTools {

	private static final Logger log = Logger.getLogger(LinkTools.class.getName());
	
	private static final String OS_MACOS = "Mac OS";
	private static final String OS_WINDOWS = "Windows";
	 
	private static final String[] UNIX_BROWSE_CMDS = {
	          "www-browser", // debian update-alternatives target
	          "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape", "w3m", "lynx"};
	 
	private static final String[] UNIX_OPEN_CMDS = {
	          "run-mailcap", // many Unixes, run registered program from /etc/mailcap
	          // Fall back to assuming it's a text file.
	          "pager", // debian update-alternatives target
	          "less", "more"};
	
	public static void browse(final URL url) throws IOException
	  {
	    // Try Java 1.6 Desktop class if supported
	    if (browseDesktop(url)) return;
	 
	    final String osName = System.getProperty("os.name");
	    log.finer("Launching " + url + " for OS " + osName);
	 
	    if (osName.startsWith(OS_MACOS)) {
	      browseMac(url);
	    }
	    else if (osName.startsWith(OS_WINDOWS)) {
	      browseWindows(url);
	    }
	    else {
	      //assume Unix or Linux
	      browseUnix(url);
	    }
	  }
	
	public static void browseAndWarn(final URL url, final Component parentComponent)
	  {
	    try {
	      browse(url);
	    }
	    catch (final IOException e) {
	      log.log(Level.SEVERE, "Unable to browse to " + url, e);
	      SwingUtilities.invokeLater(new Runnable()
	      {
	        public void run()
	        {
	          JOptionPane.showMessageDialog(parentComponent, "Couldn't open a web browser:\n" + e.getLocalizedMessage(),
	                  "Unable to launch web browser", JOptionPane.ERROR_MESSAGE);
	        }
	      });
	    }
	  }
	
	 public static void browseAndWarn(final String url, final Component parentComponent)
	  {
	    try {
	      browse(new URL(url));
	    }
	    catch (final IOException e) {
	      log.log(Level.SEVERE, "Unable to browse to " + url, e);
	      SwingUtilities.invokeLater(new Runnable()
	      {
	        public void run()
	        {
	          JOptionPane.showMessageDialog(parentComponent, "Couldn't open a web browser:\n" + e.getLocalizedMessage(),
	                  "Unable to launch web browser", JOptionPane.ERROR_MESSAGE);
	        }
	      });
	    }
	  }
	 
	 public static void open(final File file) throws IOException
	  {
	    // Try Java 1.6 Desktop class if supported
	    if (openDesktop(file)) return;
	 
	    final String osName = System.getProperty("os.name");
	    log.finer("Opening " + file + " for OS " + osName);
	 
	    if (osName.startsWith(OS_MACOS)) {
	      openMac(file);
	    }
	    else if (osName.startsWith(OS_WINDOWS)) {
	      openWindows(file);
	    }
	    else {
	      //assume Unix or Linux
	      openUnix(file);
	    }
	  }
	 
	 public static void openAndWarn(final File file, final Component parentComponent)
	  {
	    try {
	      open(file);
	    }
	    catch (final IOException e) {
	      log.log(Level.SEVERE, "Unable to open " + file, e);
	      SwingUtilities.invokeLater(new Runnable()
	      {
	        public void run()
	        {
	          JOptionPane.showMessageDialog(parentComponent, "Couldn't open " + file + ":\n" + e.getLocalizedMessage(),
	                  "Unable to open file", JOptionPane.ERROR_MESSAGE);
	        }
	      });
	    }
	  }
	 
	 private static boolean browseDesktop(final URL url) throws IOException
	  {
	    final Class desktopClass = getDesktopClass();
	    if (desktopClass == null) return false;
	 
	    final Object desktopInstance = getDesktopInstance(desktopClass);
	    if (desktopInstance == null) return false;
	 
	    log.finer("Launching " + url + " using Desktop.browse()");
	 
	    try {
	      final Method browseMethod = desktopClass.getDeclaredMethod("browse", URI.class);
	      browseMethod.invoke(desktopInstance, new URI(url.toExternalForm()));
	      return true;
	    }
	    catch (InvocationTargetException e) {
	      if (e.getCause() instanceof IOException) {
	        throw (IOException) e.getCause();
	      }
	      else {
	        log.log(Level.FINE, "Exception in Desktop operation", e);
	        return false;
	      }
	    }
	    catch (Exception e) {
	      log.log(Level.FINE, "Exception in Desktop operation", e);
	      return false;
	    }
	  }
	 
	 private static void browseWindows(final URL url) throws IOException
	  {
	    log.finer("Windows invoking rundll32");
	    Runtime.getRuntime().exec(new String[]{"rundll32", "url.dll,FileProtocolHandler", url.toString()});
	  }
	 
	 private static void browseUnix(final URL url) throws IOException
	  {
	    for (final String cmd : UNIX_BROWSE_CMDS) {
	      log.finest("Unix looking for " + cmd);
	      if (unixCommandExists(cmd)) {
	        log.finer("Unix found " + cmd);
	        Runtime.getRuntime().exec(new String[]{cmd, url.toString()});
	        return;
	      }
	    }
	    throw new IOException("Could not find a suitable web browser");
	  }
	 
	 private static void browseMac(final URL url) throws IOException
	  {
	    try {
	      final Class fileMgr = getAppleFileManagerClass();
	      final Method openURL = fileMgr.getDeclaredMethod("openURL", String.class);
	 
	      log.finer("Mac invoking");
	      openURL.invoke(null, url.toString());
	    }
	    catch (Exception e) {
	      log.log(Level.WARNING, "Couldn't launch Mac URL", e);
	      throw new IOException("Could not launch Mac URL: " + e.getLocalizedMessage());
	    }
	  }
	 
	 private static boolean openDesktop(final File file) throws IOException
	  {
	    final Class desktopClass = getDesktopClass();
	    if (desktopClass == null) return false;
	 
	    final Object desktopInstance = getDesktopInstance(desktopClass);
	    if (desktopInstance == null) return false;
	 
	    log.finer("Opening " + file + " using Desktop.open()");
	 
	    try {
	      final Method browseMethod = desktopClass.getDeclaredMethod("open", File.class);
	      browseMethod.invoke(desktopInstance, file);
	      return true;
	    }
	    catch (InvocationTargetException e) {
	      if (e.getCause() instanceof IOException) {
	        throw (IOException) e.getCause();
	      }
	      else if (e.getCause() instanceof IllegalArgumentException) {
	        throw new FileNotFoundException(e.getCause().getLocalizedMessage());
	      }
	      else {
	        log.log(Level.FINE, "Exception in Desktop operation", e);
	        return false;
	      }
	    }
	    catch (Exception e) {
	      log.log(Level.FINE, "Exception in Desktop operation", e);
	      return false;
	    }
	  }
	 
	 private static void openWindows(final File file) throws IOException
	  {
	    log.finer("Windows invoking rundll32");
	    Runtime.getRuntime().exec(new String[]{"rundll32", "shell32.dll,ShellExec_RunDLL", file.getAbsolutePath()});
	  }
	 
	 private static void openMac(final File file) throws IOException
	  {
	    // we use openURL() on the file's URL form since openURL supports file:// protocol
	    browseMac(file.getAbsoluteFile().toURL());
	  }
	 
	 private static void openUnix(final File file) throws IOException
	  {
	    for (final String cmd : UNIX_OPEN_CMDS) {
	      log.finest("Unix looking for " + cmd);
	      if (unixCommandExists(cmd)) {
	        log.finer("Unix found " + cmd);
	        Runtime.getRuntime().exec(new String[]{cmd, file.getAbsolutePath()});
	        return;
	      }
	    }
	    throw new IOException("Could not find a suitable viewer");
	  }
	 
	 private static Class getDesktopClass()
	  {
	    // NB The following String is intentionally not inlined to prevent ProGuard trying to locate the unknown class.
	    final String desktopClassName = "java.awt.Desktop";
	    try {
	      return Class.forName(desktopClassName);
	    }
	    catch (ClassNotFoundException e) {
	      log.fine("Desktop class not found");
	      return null;
	    }
	  }
	 
	 private static Object getDesktopInstance(final Class desktopClass)
	  {
	    try {
	      final Method isDesktopSupportedMethod = desktopClass.getDeclaredMethod("isDesktopSupported");
	      log.finest("invoking isDesktopSupported");
	      final boolean isDesktopSupported = (Boolean) isDesktopSupportedMethod.invoke(null);
	 
	      if (!isDesktopSupported) {
	        log.finer("isDesktopSupported: no");
	        return null;
	      }
	 
	      final Method getDesktopMethod = desktopClass.getDeclaredMethod("getDesktop");
	      return getDesktopMethod.invoke(null);
	    }
	    catch (Exception e) {
	      log.log(Level.FINE, "Exception in Desktop operation", e);
	      return null;
	    }
	  }
	 
	 private static Class getAppleFileManagerClass() throws ClassNotFoundException
	  {
	    log.finest("Mac looking for com.apple.eio.FileManager");
	 
	    // NB The following String is intentionally not inlined to prevent ProGuard trying to locate the unknown class.
	    final String appleClass = "com.apple.eio.FileManager";
	    return Class.forName(appleClass);
	  }
	 
	 private static boolean unixCommandExists(final String cmd) throws IOException
	  {
	    final Process whichProcess = Runtime.getRuntime().exec(new String[]{"which", cmd});
	 
	    boolean finished = false;
	    do {
	      try {
	        whichProcess.waitFor();
	        finished = true;
	      }
	      catch (InterruptedException e) {
	        log.log(Level.WARNING, "Interrupted waiting for which to complete", e);
	      }
	    } while (!finished);
	 
	    return whichProcess.exitValue() == 0;
	  }
	 
	 public static void email(String emailAdd) throws Exception {
		    String cmd = "start mailto:" + emailAdd;
		    Runtime.getRuntime().exec(cmd);
		}

		private static String urlEncode(String s) {
		    StringBuilder sb = new StringBuilder();
		    for (int i = 0; i < s.length(); i++) {
		        char ch = s.charAt(i);
		        if (Character.isLetterOrDigit(ch)) {
		            sb.append(ch);
		        }
		        else {
		            sb.append(String.format("%%%02X", (int)ch));
		        }
		    }
		    return sb.toString();
		}
		
		 public static boolean sendMail(final String url) throws IOException
		  {
		    log.finer("Mail: " + url + " using Desktop.mail()");
		 
		    try {
		    if(Desktop.isDesktopSupported()) {
		      Desktop d = java.awt.Desktop.getDesktop();
		      d.mail(new URI(url));
		      return true;
		    } else {
		    	JOptionPane.showMessageDialog(null, "Your OS or Java does not support automatic mail sending.\nYou can manually send a mail to wolvan1@gmail.com", "OS not supported!", 0, null);
		    	return false;
		    }
		    }
		    catch (Exception e) {
		      log.log(Level.FINE, "Exception in Desktop operation", e);
		      return false;
		    }
		  }
		 
		 public static String getWebsiteSource(String url) throws IOException {
			 URL yahoo = new URL(url);
	            URLConnection yc = yahoo.openConnection();
	            BufferedReader in = new BufferedReader(new InputStreamReader(
	                    yc.getInputStream(), "UTF-8"));
	            String inputLine;
	            StringBuilder a = new StringBuilder();
	            while ((inputLine = in.readLine()) != null)
	                a.append(inputLine);
	            in.close();

	            return a.toString();
		 }
}
